//定义装饰器相关信息
const KRouter = require('koa-router')
const glob = require('glob')
const _ = require('loadsh')
const { resolve } = require('path')
const symbolPrefix = Symbol('prefix')
const routerMap = new Map()
const isArray = c=>_.isArray(c)?c:[c] //判断是否是数组
export class Router{
    constructor(app,apiPath){
        this.app = app
        this.apiPath = apiPath
        this.router = new KRouter()
    }

    init(){ //遍历routes下的所有js
        glob.sync(resolve(this.apiPath,'./**/*.js')).forEach(require)
        //循环生成例如this.router.get这样的路由
        //此处的conf={target:target,...conf} 此处的controller=target[key]
        for(let [conf,controller] of routerMap){//遍历+数组解构
            const controllers = isArray(controller)
            let prefixPath = conf.target[symbolPrefix] //controller上的地址
            if(prefixPath) prefixPath = normalizePath(prefixPath)
            const routerPath = prefixPath + conf.path //controller上的地址+get或者post上的地址
            this.router[conf.method](routerPath,...controllers)//controllers针对的是相同路径的不同名方法
        }
        //配置使用路由
        this.app.use(this.router.routes())
        this.app.use(this.router.allowedMethods())
    }
}
//判断path，以/开头就不加/，没有就加/
const normalizePath = path=>path.startsWith('/')?path:`/${path}`

/**
 * target ： 作用的对象，有如下情况：
作用于 class 时，target 为该 class 函数
作用于 class 中的函数、属性时，target 为该 class 的 prototype 对象
作用于 对象字面量中的函数、属性 时，target 为该对象
prop ： 描述的属性名，若decorator作用于class时，该参数为空
descriptor ： 属性原本的描述符，该描述符可通过 Object.getOwnPropertyDescriptor() 获取，若decorator作用于class时，该参数为空
 */

//将相关信息存入routerMap中,此处的conf={method:'post',path:path}
const router = conf=>(target,key,descriptor)=>{
    conf.path = normalizePath(conf.path)
    routerMap.set({
        target:target,
        ...conf
    },target[key]) //key为函数名，target[key]为函数的引用
}

//定义controller装饰器（在用装饰器装饰类的时候，只能够接受一个参数——target）
export const controller = path=> target=> (target.prototype[symbolPrefix]=path)

//get装饰器用于获取数据（装饰器还可以接受参数，返回一个符合装饰器规范的新函数即可，这样又可以对装饰器的装饰行为进行定制了）
export const get = path=>router({
    method:'get',
    path:path
})
//post装饰器用于新增数据
export const post = path=>router({
    method:'post',
    path:path
})
//put装饰器用于更新已有数据
export const put = path=>router({
    method:'put',
    path:path
})
//del装饰器用于删除数据
export const del = path=>router({
    method:'del',
    path:path
})
//使用中间件
export const use = path=>router({
    method:'use',
    path:path
})
//all装饰器处理所有的请求
export const all = path=>router({
    method:'all',
    path:path
})